package at.com.vn.chess.servlet;

import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.lang.reflect.Constructor;
import java.rmi.RemoteException;
import java.util.ArrayList;
import java.util.Observable;
import java.util.Observer;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.*;

import org.apache.log4j.Logger;

import vn.com.bip.office.smart.util.XMLHelper;

import at.com.vn.chess.bean.PrivateChatRoomBean;
import at.com.vn.chess.entity.ChatUser;
import at.com.vn.chess.entity.ChessRow;
import at.com.vn.chess.entity.User;
import at.com.vn.chess.service.BaseService;
import at.com.vn.chess.util.Const;

/**
 * Simple demonstration for an Applet <-> Servlet communication.
 */
public abstract class ChatServletBase extends HttpServlet {
	private static final long serialVersionUID = 1L;
	
	Logger logger;

	private String inputStrXml = "";

	private String outputStrXml = "";

	// source acts as the distributor of new messages
	MessageSource source = new MessageSource();

	ArrayList<User> loginUserList = new ArrayList<User>();
	ArrayList<ChatUser> chatUserList = new ArrayList<ChatUser>();
	ArrayList<ChessRow> chessRowList = new ArrayList<ChessRow>();
	ArrayList<PrivateChatRoomBean> privateChatRoomBeanList = new ArrayList<PrivateChatRoomBean>();

	ArrayList<String> chessBoardInfoList = new ArrayList<String>();
	ArrayList<String> chessBoardHistoryList = new ArrayList<String>();
	
	public ChatServletBase() {
		super();
		logger = Logger.getLogger( getClass() );
		logger.info( "Bat dau" );
	}

	// doGet() returns the next message. It blocks until there is one.
	public abstract void doGet(HttpServletRequest req, HttpServletResponse res)
			throws ServletException, IOException;

	// doGet() returns the next message. It blocks until there is one.
	public void getChat(HttpServletRequest req, HttpServletResponse res)
			throws ServletException, IOException {
		res.setContentType("application/x-www-form-urlencoded");

		res.setCharacterEncoding("UTF-8");
		PrintWriter out = res.getWriter();

		// Return the next message (blocking)
		out.println(getNextMessage());
	}

	public abstract void doPost(HttpServletRequest request,
			HttpServletResponse response) throws ServletException, IOException;

	@SuppressWarnings( "unchecked" )
	public void postChat(HttpServletRequest request,
			HttpServletResponse response) throws ServletException, IOException {

		boolean isSendBroadcast = true;

		try {
			// get message from Applet
			request.setCharacterEncoding("UTF-8");
			// Accept the new message as the "broadcast" parameter
			String xmlInfo = request.getParameter("broadcast");

			if (xmlInfo == null || xmlInfo.equals("")) {
				isSendBroadcast = false;
				// read a String-object from applet
				// instead of a String-object, you can transmit any object,
				// which
				// is known to the servlet and to the applet
				InputStream in = request.getInputStream();
				ObjectInputStream inputFromApplet = new ObjectInputStream(in);
				xmlInfo = (String) inputFromApplet.readObject();
			}
			inputStrXml = xmlInfo;

			ServletContext context = getServletConfig().getServletContext();
			loginUserList = (ArrayList<User>) context
					.getAttribute("LoginUserList");
			if (loginUserList == null) {
				loginUserList = new ArrayList<User>();
			}

			BaseService chessService = null;

			// Initation Service Classes
			String processName = XMLHelper.getProcessName(inputStrXml);
			String clsName = processName.substring(0, processName.indexOf('_'))
					+ "Service";
			Class<?> cls = Class.forName(Const.SERVICE_PACKAGE_NAME + clsName);

			Class<?> partypes[] = new Class[1];
			partypes[0] = String.class;
			Constructor<?> ct = cls.getConstructor(partypes);
			Object arglist[] = new Object[1];
			arglist[0] = inputStrXml;

			chessService = (BaseService) ct.newInstance(arglist);

			// Set array data
			chessService.setArrayData(loginUserList, chatUserList,
					chessRowList, privateChatRoomBeanList, chessBoardInfoList,
					chessBoardHistoryList);
			// Calling service method
			chessService.doService();

			// Get xml result
			outputStrXml = chessService.getOutputStrXml();

			// Send content to Client
			response.setCharacterEncoding("UTF-8");
			if (isSendBroadcast) {
				// Broadcast message to all client
				broadcastMessage(outputStrXml);
				// Set the status code to indicate there will be no response
				response.setStatus(HttpServletResponse.SC_NO_CONTENT);
			} else {
				response.setContentType("application/x-java-serialized-object");
				OutputStream outstr = response.getOutputStream();
				ObjectOutputStream oos = new ObjectOutputStream(outstr);
				// send data
				oos.writeObject(outputStrXml);
				oos.flush();
				oos.close();
			}

		} catch (Throwable e) {
			logger.error( e.getStackTrace(), e );
			System.err.println(e);

		}
	}

	public String getNextMessage() throws RemoteException {
		// Create a message sink to wait for a new message from the
		// message source.
		return new MessageSink().getNextMessage(source);
	}

	public void broadcastMessage(String message) throws RemoteException {
		// Send the message to all the HTTP-connected clients by giving the
		// message to the message source
		source.sendMessage(message);

	}

	// MessageSource acts as the source for new messages.
	// Clients interested in receiving new messages can
	// observe this object.
	class MessageSource extends Observable {
		public void sendMessage(String message) {
			setChanged();
			notifyObservers(message);
		}
	}

	// MessageSink acts as the receiver of new messages.
	// It listens to the source.
	class MessageSink implements Observer {

		String message = null; // set by update() and read by getNextMessage()

		// Called by the message source when it gets a new message
		synchronized public void update(Observable o, Object arg) {
			// Get the new message
			message = (String) arg;

			// Wake up our waiting thread
			notify();
		}

		// Gets the next message sent out from the message source
		synchronized public String getNextMessage(MessageSource source) {
			// Tell source we want to be told about new messages
			source.addObserver(this);

			// Wait until our update() method receives a message
			while (message == null) {
				try {
					wait();
				} catch (Exception ignored) {
				}
			}

			// Tell source to stop telling us about new messages
			source.deleteObserver(this);

			// Now return the message we received
			// But first set the message instance variable to null
			// so update() and getNextMessage() can be called again.
			String messageCopy = message;
			message = null;
			return messageCopy;
		}
	}
}
